using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using System.Xml;
using Alerts_Administration_2010.interfaces;

namespace Alerts_Administration_2010.classes
{
    public class AlertWrapper : IAlertWrapper
    {
        #region private interface implementations
        private class _TemplateFilter : IAlertTemplateFilter
        {
            private XmlNode _templateFilterQuery;
            private string _templateFilterFriendlyName;
            private int _index = -1;

            public _TemplateFilter(XmlNode filterNode, int index)
            {
                _index = index;
                if (filterNode == null) return;

                string s = filterNode.SelectSingleNode("FriendlyName").InnerText;
                StringBuilder sb = new StringBuilder();
                foreach (string ss in s.Split(';'))
                {
                    sb.Append(Microsoft.SharePoint.Utilities.SPUtility.GetLocalizedString(ss, "core", SPContext.Current.Web.Language));
                }
                _templateFilterFriendlyName = sb.ToString();
                _templateFilterQuery = filterNode.SelectSingleNode("Query");
            }

            public static IAlertTemplateFilter DefaultFilter
            {
                get
                {
                    _TemplateFilter defaultFilter = new _TemplateFilter(null, 0);
                    defaultFilter._templateFilterFriendlyName = "Anything changes";
                    XmlDocument doc = new XmlDocument();
                    defaultFilter._templateFilterQuery = doc.CreateNode(XmlNodeType.Text, "", "");
                    return defaultFilter;
                }
            }

            public string FriendlyName { get { return _templateFilterFriendlyName; } }
            public XmlNode QueryNode { get { return _templateFilterQuery; } }
            public int Index { get { return _index; } }
        }
        private class _TemplateFilterList : IAlertTemplateFilterList
        {
            private class _TemplateFilterEnumerator : IEnumerator<IAlertTemplateFilter>
            {
                private _TemplateFilterList _templateFilterList;
                private int _currentIndex = -1;

                public _TemplateFilterEnumerator(_TemplateFilterList templateFilterList)
                {
                    _templateFilterList = templateFilterList;
                }

                #region IEnumerator<ITemplateFilter> Members

                public IAlertTemplateFilter Current
                {
                    get { return _templateFilterList[_currentIndex]; }
                }

                #endregion

                #region IDisposable Members

                public void Dispose()
                {
                    // nothing to do here;
                }

                #endregion

                #region IEnumerator Members

                object System.Collections.IEnumerator.Current
                {
                    get { return ((IEnumerator<IAlertTemplateFilter>)this).Current; }
                }

                public bool MoveNext()
                {
                    _currentIndex++;
                    if (_currentIndex < _templateFilterList.Count)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }

                public void Reset()
                {
                    _currentIndex = -1;
                }

                #endregion
            }

            private List<IAlertTemplateFilter> _templateFilters = new List<IAlertTemplateFilter>();
            private XmlNodeList _templateFilterNodes = null;
            private int _count = -1;
            private int _countDefaults;
            private SPAlert _alert;

            private XmlNodeList TemplateFilterNodes
            {
                get
                {
                    if (_templateFilterNodes == null)
                    {
                        XmlDocument doc = new System.Xml.XmlDocument();
                        doc.LoadXml(_alert.AlertTemplate.Xml);

                        List<XmlNode> valueNodesToBeReplaced = new List<XmlNode>();

                        foreach (XmlNode UserIDNode in doc.SelectNodes("/AlertTemplate/Filters//Value[@type='integer']/UserID"))
                        {
                            valueNodesToBeReplaced.Add(UserIDNode.ParentNode);
                        }

                        foreach (XmlNode oldChildValueNode in valueNodesToBeReplaced)
                        {
                            XmlNode newChildValueNode = doc.CreateNode(XmlNodeType.Element, "Value", "");
                            XmlNode newChildUserNode = doc.CreateNode(XmlNodeType.Text, "", "");
                            XmlAttribute typeAttribute = doc.CreateAttribute("type");
                            typeAttribute.Value = "string";
                            newChildUserNode.Value = _alert.User.Name.ToLower();
                            newChildValueNode.Attributes.Append(typeAttribute);
                            newChildValueNode.AppendChild(newChildUserNode);

                            oldChildValueNode.ParentNode.ReplaceChild(newChildValueNode, oldChildValueNode);

                        }

                        _templateFilterNodes = doc.SelectNodes("/AlertTemplate/Filters/FilterDefinition");
                    }
                    return _templateFilterNodes;
                }
            }

            public int Count
            {
                get
                {
                    if (_count == -1)
                    {
                        _count = TemplateFilterNodes.Count + _countDefaults;
                    }

                    return _count;
                }
            }

            public IAlertTemplateFilter this[int index]
            { 
                get
                {
                    if (index >= Count) throw new ArgumentOutOfRangeException();
                    if (index >= _templateFilters.Count)
                    {
                        _templateFilters.Add(new _TemplateFilter(TemplateFilterNodes[index - _countDefaults], index));
                    }
                    return _templateFilters[index];
                }
            }

            public _TemplateFilterList(SPAlert alert)
            {
                _alert = alert;
                _templateFilters.Add(_TemplateFilter.DefaultFilter);

                _countDefaults = _templateFilters.Count;
            }

            #region IEnumerable<ITemplateFilter> Members

            public IEnumerator<IAlertTemplateFilter> GetEnumerator()
            {
                return new _TemplateFilterEnumerator(this);
            }

            #endregion

            #region IEnumerable Members

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return new _TemplateFilterEnumerator(this);
            }

            #endregion
        }
        #endregion

        #region private fields
        private SPAlert _alert = null;
        private XmlNode _filterQueryNode = null;
        private IAlertTemplateFilterList _templateFilterList = null;
        private IAlertTemplateFilter _selectedTemplateFilter = null;
        #endregion

        #region private properties
        private XmlNode FilterQueryNode
        {
            get
            {
                if (_filterQueryNode == null)
                {
                    XmlDocument _filter = new XmlDocument();
                    if (_alert.Filter == string.Empty)
                    {
                        _filterQueryNode = _filter.CreateNode(XmlNodeType.Text, "", "");
                    }
                    else
                    {
                        _filter.LoadXml(_alert.Filter);
                        _filterQueryNode = _filter.SelectSingleNode("/Query");
                    }
                }
                return _filterQueryNode;
            }
        }
        #endregion

        #region public properties
        public IAlertTemplateFilterList TemplateFilterList 
        { 
            get 
            {
                if (_templateFilterList == null)
                {
                    _templateFilterList = new _TemplateFilterList(_alert);
                }
                return _templateFilterList; 
            }
        }
        public IAlertTemplateFilter SelectedTemplateFilter
        {
            get
            {
                if (_selectedTemplateFilter == null)
                {
                    _selectedTemplateFilter = _TemplateFilter.DefaultFilter;
                    foreach (IAlertTemplateFilter filter in TemplateFilterList)
                    {
                        if (CompareNodes(filter.QueryNode, FilterQueryNode))
                        {
                            _selectedTemplateFilter = filter;
                            break;
                        }
                    }
                }
                return _selectedTemplateFilter;
            }
        }
        public SPAlert Alert { get { return _alert; } }
        #endregion

        #region ctors
        public AlertWrapper(SPAlert alert)
        {
            _alert = alert;
        }
        public AlertWrapper(SPWeb site, string alertId)
        {
            _alert = GetAlertById(site, new Guid(alertId));
            if (_alert == null) throw new AlertNotFoundException();
        }
        #endregion

        // ToDo: move this to an own class!
        private static bool CompareNodes(System.Xml.XmlNode node1, System.Xml.XmlNode node2)
        {
            if ((node1.NamespaceURI == node2.NamespaceURI) &&
                (node1.Name == node2.Name) &&
                (node1.NodeType == node2.NodeType) &&
                (node1.ChildNodes.Count == node2.ChildNodes.Count))
            {
                if (node1.ChildNodes.Count == 0)
                {
                    #region switch NodeType
                    switch (node1.NodeType)
                    {
                        case System.Xml.XmlNodeType.Attribute:
                            throw new NotImplementedException("Filter couldn't be compared because NodeType 'Attribute' was not implemented.");
                        case System.Xml.XmlNodeType.CDATA:
                            throw new NotImplementedException("Filter couldn't be compared because NodeType 'CDATA' was not implemented.");
                        case System.Xml.XmlNodeType.Comment:
                            throw new NotImplementedException("Filter couldn't be compared because NodeType 'Comment' was not implemented.");
                        case System.Xml.XmlNodeType.Document:
                            throw new NotImplementedException("Filter couldn't be compared because NodeType 'Document' was not implemented.");
                        case System.Xml.XmlNodeType.DocumentFragment:
                            throw new NotImplementedException("Filter couldn't be compared because NodeType 'DocumentFragment' was not implemented.");
                        case System.Xml.XmlNodeType.DocumentType:
                            throw new NotImplementedException("Filter couldn't be compared because NodeType 'DocumentType' was not implemented.");
                        case System.Xml.XmlNodeType.Element:
                            return true;
                        case System.Xml.XmlNodeType.EndElement:
                            throw new NotImplementedException("Filter couldn't be compared because NodeType 'EndElement' was not implemented.");
                        case System.Xml.XmlNodeType.EndEntity:
                            throw new NotImplementedException("Filter couldn't be compared because NodeType 'EndEntity' was not implemented.");
                        case System.Xml.XmlNodeType.Entity:
                            throw new NotImplementedException("Filter couldn't be compared because NodeType 'Entity' was not implemented.");
                        case System.Xml.XmlNodeType.EntityReference:
                            throw new NotImplementedException("Filter couldn't be compared because NodeType 'EntityReference' was not implemented.");
                        case System.Xml.XmlNodeType.None:
                            throw new NotImplementedException("Filter couldn't be compared because NodeType 'None' was not implemented.");
                        case System.Xml.XmlNodeType.Notation:
                            throw new NotImplementedException("Filter couldn't be compared because NodeType 'Notation' was not implemented.");
                        case System.Xml.XmlNodeType.ProcessingInstruction:
                            throw new NotImplementedException("Filter couldn't be compared because NodeType 'ProcessingInstruction' was not implemented.");
                        case System.Xml.XmlNodeType.SignificantWhitespace:
                            throw new NotImplementedException("Filter couldn't be compared because NodeType 'SignificantWhitespace' was not implemented.");
                        case System.Xml.XmlNodeType.Text:
                            return node1.Value == node2.Value;
                        case System.Xml.XmlNodeType.Whitespace:
                            throw new NotImplementedException("Filter couldn't be compared because NodeType 'Whitespace' was not implemented.");
                        case System.Xml.XmlNodeType.XmlDeclaration:
                            throw new NotImplementedException("Filter couldn't be compared because NodeType 'XmlDeclaration' was not implemented.");
                        default:
                            throw new NotImplementedException("Filter couldn't be compared because NodeType is unknown.");
                    }
                    #endregion
                }
                else
                {
                    for (int i = 0; i < node1.ChildNodes.Count; i++)
                    {
                        bool b = CompareNodes(node1.ChildNodes[i], node2.ChildNodes[i]);
                        if (!b) return false;
                    }
                    return true;
                }
            }
            else
            {
                return false;
            }

        }

        public static SPAlert GetAlertById(SPWeb site, Guid alertId)
        {
            SPAlert alert = null;
            foreach (SPAlert a in site.Alerts)
            {
                if (a.ID == alertId)
                {
                    alert = a;
                    break;
                }
            }
            return alert;
        }
    }

    public class AlertNotFoundException : Exception { }
}
